#!/usr/bin/python
# -*- coding: utf-8 -*-

import pygst
pygst.require("0.10")
import gst
import gobject

import threading
from signal import *
import time

# RPC-JSON
import utils.jsonrpc as jsonrpc
from os import path, unlink


IP_VIDEOSERVER = "10.193.250.223"
LOCALHOST = "127.0.0.1" # don't work when write "localhost"
ROOT_DIR = path.dirname(__file__)
UNIXSOCKET = path.join(ROOT_DIR,"socketipc")


gobject.threads_init()
        

class Forwarder:
    def __init__(self, rtp_recv_port, caps):
        self.caps = caps
        self.rtp_recv_port = rtp_recv_port
        self.rtcp_recv_port = self.rtp_recv_port + 1
        self.rtcp_send_port = self.rtp_recv_port + 5

    def bus_handler(self, unused_bus, message):
        if message.type == gst.MESSAGE_ERROR:
            print message
        return gst.BUS_PASS
        
        
    def run(self):          
        # Create a Pipeline          
        self.pipeline = gst.Pipeline()
        
        #Connect to the bus to get the messages
        bus = self.pipeline.get_bus()
        bus.add_signal_watch()
        bus.connect("message", self.bus_handler)
        
        # the udp src and source we will use for self.RTP and self.RTCP
        rtpsrc = gst.element_factory_make('udpsrc', 'rtpsrc')
        rtpsrc.set_property('port', self.rtp_recv_port)

        # we need to set caps on the udpsrc for the self.RTP data
        rtpsrc.set_property('caps', gst.caps_from_string(self.caps))
        
        rtcpsrc = gst.element_factory_make('udpsrc', 'rtcpsrc')
        rtcpsrc.set_property('port', self.rtcp_recv_port)

        rtcpsink = gst.element_factory_make('udpsink', 'rtcpsink')
        rtcpsink.set_property('port', self.rtcp_send_port)
        rtcpsink.set_property('host', IP_VIDEOSERVER)
        
        # no need for synchronisation or preroll on the self.RTCP sink
        rtcpsink.set_property('async', False)
        rtcpsink.set_property('sync', False) 

        self.pipeline.add(rtpsrc, rtcpsrc, rtcpsink)
        
        # the rtpbin element
        rtpbin = gst.element_factory_make('gstrtpbin', 'rtpbin') 
        self.pipeline.add(rtpbin)
        
        # now link all to the rtpbin, start by getting an self.RTP sinkpad for session 0
        srcpad = gst.Element.get_static_pad(rtpsrc, 'src')
        sinkpad = gst.Element.get_request_pad(rtpbin, 'recv_rtp_sink_0')
        gst.Pad.link(srcpad, sinkpad)

        # get an RTCP sinkpad in session 0
        srcpad = gst.Element.get_static_pad(rtcpsrc, 'src')
        sinkpad = gst.Element.get_request_pad(rtpbin, 'recv_rtcp_sink_0')
        gst.Pad.link(srcpad, sinkpad)

        # get an RTCP srcpad for sending RTCP back to the sender
        srcpad = gst.Element.get_request_pad(rtpbin, 'send_rtcp_src_0')
        sinkpad = gst.Element.get_static_pad(rtcpsink, 'sink')
        gst.Pad.link(srcpad, sinkpad)
        

        ## Add server
        rtpbinOut = gst.element_factory_make("gstrtpbin", "rtpbinout")
        
        self.queue = gst.element_factory_make("queue", "Vbuffer")
        
        self.udpsinkRTP = gst.element_factory_make("multiudpsink", "udpsinkRTP")
        self.udpsinkRTCP = gst.element_factory_make("multiudpsink", "udpsinkRTCP")
        
        self.udpsinkRTCP.set_property("async", False)
        self.udpsinkRTCP.set_property("sync", False)
        
        self.pipeline.add(self.queue, self.udpsinkRTP, self.udpsinkRTCP, rtpbinOut)
        
        self.queue.link_pads("src", rtpbinOut, "send_rtp_sink_3")
        rtpbinOut.link_pads("send_rtp_src_3", self.udpsinkRTP, "sink")
        rtpbinOut.link_pads("send_rtcp_src_3", self.udpsinkRTCP, "sink")
        
        rtpbin.connect('pad-added', self.pad_added_cb) 
        rtpbin.connect('pad-removed', self.pad_removed_cb) 
        self.udpsinkRTP.connect('client-added', self.client_added)
        self.udpsinkRTP.connect('client-removed', self.client_removed)        
        
        self.pipeline.set_state(gst.STATE_NULL)


    def getState(self):
        return self.pipeline.get_state()[1]
    
    def pad_added_cb(self, rtpbin, new_pad):
        print "Pad added in RTPBIN"
        
        qv_pad = self.queue.get_pad("sink")
        if qv_pad.is_linked():
            pad = qv_pad.get_peer()
            pad.unlink( qv_pad )
        new_pad.link(qv_pad)

    def pad_removed_cb(self, rtpbin, new_pad):
        print "removed pad"
    
    
    def client_added(self, udpsinkRTP, ip, port):
        print "New client: (%s:%d)" % (ip, port)
        if (self.getState() == gst.STATE_NULL):
            self.play() 
            
    def client_removed(self, udpsinkRTP, ip, port):
        print "Remove client: (%s:%d)" % (ip, port)
        if (self.getState() == gst.STATE_PLAYING) and (not "," in self.udpsinkRTP.get_property("clients") ):
            self.stop()

    def addWatcher(self, dest, port):
        port = int(port)
        rtcpport = port + 1
        self.udpsinkRTP.emit("add", dest, port)
        self.udpsinkRTCP.emit("add", dest, rtcpport)
        return False # Return false so gobject.idle_add won't call several time the same
        
    def removeWatcher(self, dest, port):
        port = int(port)
        rtcpport = port + 1
        self.udpsinkRTP.emit("remove", dest, port)
        self.udpsinkRTCP.emit("remove", dest, rtcpport)
        return False    
        
    def play(self):
        print "Play"
        self.pipeline.set_state(gst.STATE_PLAYING)
        
    def stop(self):
        print "stop"
        self.pipeline.set_state(gst.STATE_NULL)
        
        
        
class ChannelManager:
    def __init__(self, channelID):
        self.channelID = channelID
        
        rtp_recv_port = (5+channelID)*1000
        self.videoForwarder = Forwarder(rtp_recv_port, "application/x-rtp,media=(string)video,clock-rate=(int)90000,encoding-name=(string)H263-1998")

        #I'm sure we don't need two pipelines..
        rtp_recv_port_audio = rtp_recv_port + 50
        self.audioForwarder = Forwarder(rtp_recv_port_audio, "application/x-rtp,media=(string)audio,clock-rate=(int)8000,encoding-name=(string)AMR,encoding-params=(string)1,octet-align=(string)1")

        self.videoForwarder.run()
        self.audioForwarder.run()
            
    def addWatcher(self, dest, port):
        print "Add watcher"
        self.videoForwarder.addWatcher(dest, port)
        self.audioForwarder.addWatcher(dest, port+50)
        return False # Return false so gobject.idle_add won't call several time the same
        
    def removeWatcher(self, dest, port):
        print "Remove watcher"
        self.videoForwarder.removeWatcher(dest, port)
        self.audioForwarder.removeWatcher(dest, port+50)
        return False
 

    def play(self):
        print "Start playing video"
        self.videoForwarder.play()
        self.audioForwarder.play()
        
    def stop(self):
        print "Stop playing video"
        self.videoForwarder.stop()
        self.audioForwarder.stop()

        
class MainProg:
    def __init__(self):
        self.listChannels = {}
        self.listUsers = {}
        
        if path.exists(UNIXSOCKET):
            unlink(UNIXSOCKET)
        self.server = jsonrpc.Server(jsonrpc.JsonRpc20(), jsonrpc.TransportUnixSocket(addr=UNIXSOCKET, logfunc=jsonrpc.log_file("logs/myrpc.log")))
        self.server.register_function( self.addWatcher, name="addwatcher" )
        self.server.register_function( self.removeWatcher, name="removewatcher" )
        self.server.register_function( self.deleteWatcher, name="deletewatcher" )

        signal(SIGINT, self.signal_cb)
        signal(SIGTERM, self.signal_cb)
       
        
    def signal_cb(self, sig, frame):
        for channel in self.listChannels.itervalues():
            channel.stop()

        self.mainloop.quit()
        
        
    def addWatcher(self, channel, ip, port):
        if not channel in self.listChannels:
            self.listChannels[channel] = ChannelManager(channelID =channel)

        if (ip, port) in self.listUsers:
            self.listChannels[ self.listUsers[(ip, port)] ].removeWatcher(ip, port)
            
        self.listUsers[(ip, port)] = channel
        self.listChannels[channel].addWatcher(ip, port)
        
        return True
        
    def removeWatcher(self, channel, ip, port):
        if not channel in listChannels:
            self.listChannels[channel] = ChannelManager(channelID =channel)
            
        gobject.idle_add( self.listChannels[channel].removeWatcher, ip, port )
        return True
        
    def deleteWatcher(self, ip, port):
        print "delete Watcher"
        if (ip, port) in self.listUsers:
            gobject.idle_add( self.listChannels[ self.listUsers[(ip, port)] ].removeWatcher, ip, port)
            
        return True
        
    def runMainloop(self):
        self.mainloop = gobject.MainLoop()
        #self.addWatcher(1, "127.0.0.1", 7000) 
        self.mainloop.run()
        
    def run(self):
        print "Homebox Video Forwarder running"
        self.mainloopThread = threading.Thread(target=self.runMainloop)
        self.mainloopThread.deamon = True
        self.mainloopThread.start()
        
        self.server.serve()

if __name__ == "__main__":
    import sys

    try:
        IP_VIDEOSERVER = sys.argv[1]
    except:
        IP_VIDEOSERVER = "127.0.0.1"

main = MainProg().run()
